package Serveur;

import java.net.*;
import java.io.*;
import java.util.*;

public class CommunicationVersClient extends Thread {
	public static int s_portRafraichissement = 7071;
	
	private ListeUsers m_listeUsers;  //reference vers la liste des clients connectés
	private User m_user;  // stocke les informations du client connecté
	
	private Socket m_sockClient;  //variable de recuperation du socket client
	private DatagramSocket m_sockRafraichissement; //socket de rafraichissement de la connection côté serveur
	
	private BufferedReader m_in = null; //
	private PrintWriter m_out = null; 
	
	//constructeur
	public CommunicationVersClient(Socket sockClient, ListeUsers listeUsers)
	{
		m_sockClient = sockClient;
		m_listeUsers = listeUsers;
		
		m_user = new User();
		m_sockRafraichissement = null;
		
		try{
		     m_in = new BufferedReader( new InputStreamReader(m_sockClient.getInputStream()));
		     m_out = new PrintWriter(m_sockClient.getOutputStream(), true);
		}catch(Exception e){}
	}
	
	//methode run du thread
	public void run()
	{ 
		try{
		        System.out.println("Un client reçu");
		        String demande = m_in.readLine();
		         
		           //traitement des demandes d'authentification et l'activation du port UDP de rafraichissement
		        if(demande.equals("connexion"))
		        {
		        	authentification();
		        	rafraichissement();		        	
		         }
		           //traitement des demandes d'adresse ip d'un client deja connectép
		        if(demande.equals("adresse"))
		         {
		           demandeAdrIp();
		         }
	        
		    }catch(Exception e){
			              System.err.println("Erreur:  communication interrompue, demande non reçu");	   
		           }	  	 
	}
	
	//methode d'ajout du membre dans la liste des membre apres authentification
	public void ajouterUser(String loginETpassword)
	{
		String tmpAdr = m_sockClient.getInetAddress().getHostAddress();
   	 
   	    StringTokenizer tokens = new StringTokenizer(loginETpassword, " ");
   	    String login = tokens.nextToken();
   	    String password = tokens.nextToken();
   	 
   	    m_user.setLogin(login);
   	    m_user.setPassWord(password);
   	    m_user.setAdrIp(tmpAdr);
   	    m_user.setStatutConnexion(true);
	    m_listeUsers.ajouterUser(m_user);  //creation et ajout d'un nouveau client connecte
	    System.out.println(login+" est connecté!");
	}
	
	//methode de verification d'existance du login et du password du client
	public boolean verificationLoginETpassword(String loginETpassword)
	{
		boolean estMembre = false;
		
		try {
				Scanner sc = new Scanner(new File("/home/etudiant/workspace/ReseauGL/src/Serveur/listeMembres.txt"));
				/** /home/etudiant/workspace/ReseauGL/src/Serveur  */
				while(sc.hasNext())
				{
					if(sc.nextLine().equals(loginETpassword))
					{
						estMembre=true;
						break;
					}
				}
		} catch (FileNotFoundException e) 
		    {
			   System.err.println("Le fichier listeMembres.txt n'existe pas !");
		    }
		
		return estMembre;
	}

	//methode d'authentification
	public void authentification()
		{       		
				while(!m_user.getStatutConnexion())
		   	    {
				  try{
				    	 String loginETpassword = m_in.readLine(); //reception de login et password du client
				    	 
					     if(verificationLoginETpassword(loginETpassword)) //verification du login et du password du client
					     { 
					    	    //ajout du client dans la liste des clients connectés
					    	 ajouterUser(loginETpassword); // creation et ajout d'un nouveau client connecte   
					    	 
					    	    //envoie de la validation de la reussite de la connexion au client
					    	 m_out.println("connecte");
					    	 
					    	    //envoie de la liste des utilisateurs connectés au client
					    	 m_out.println( m_listeUsers.getListeConnectes()); 
					    	 
					    	 int portRafraichissement = port_UDP_rafraichissement();
					    	 m_out.println( ""+portRafraichissement); 
					    	 	
					    	 //affichage des infos du client du coté serveur
					    	 m_user.afficher();
					     }else{
					    	    m_out.println("recommencez"); //login ou password du client invalide
					    	    System.out.println("Erreur: login et password:");
					          }
				     
					     Thread.sleep(100);
				    }catch(Exception e){
				    	                 System.err.println("Erreur de communication");
				                       }
		   	   }	
		}
	
	//determination d'un numero de port de rafraichissement
	public int port_UDP_rafraichissement()
	{  
		boolean estValidPort = false;
		int tmpPort = s_portRafraichissement;
	    while(!estValidPort)
	    {
	    	try
	    	{
	    	  m_sockRafraichissement = new DatagramSocket(s_portRafraichissement);
	    	  m_user.setPort(s_portRafraichissement);
	    	  tmpPort = s_portRafraichissement;
	    	  
	    	  s_portRafraichissement++;
	    	  estValidPort = true;
	    	}catch(SocketException e){
	    		       s_portRafraichissement++;
	    	        }
	    }		 
	    System.out.println("Port d'ecoute de rafraichissement est: "+s_portRafraichissement);
		return tmpPort;
	}

	//methode de rafraichissement d'une communication avec un client en UDP
	public void rafraichissement()
	{
		int taille = 1024;
		byte buffer[] = new byte[taille];		
		
		int nbPaquetRecu = 0; // compte du nombre de paquet reçu de la part du client
		int nbPaquetNonRecu = 0; //compte du nombre de paquet non reçu de la part du client
		int limitePaquet= 5; 
		
		int timeout = 2000;  //temps d'attente de rafraichissement est 2s
				
			/**  5*2 : 10 au bout de 10s si pas signe de vie, on deconnecte le client*/	
		
			while(m_user.getStatutConnexion())
	   	    {
				DatagramPacket paquetRecu = new DatagramPacket(buffer, buffer.length);  	
				DatagramPacket paquetEnvoye = null;
				
				try
				{
					if(nbPaquetNonRecu == limitePaquet)
					{
					   m_user.setStatutConnexion(false);
					   System.out.println(m_user.getLogin()+" > deconnecté");
					   
					   byte[] deconnecte = "deconnecte".getBytes();
			    	   paquetEnvoye = new DatagramPacket(deconnecte, deconnecte.length, paquetRecu.getAddress(), paquetRecu.getPort()); 
			    	   m_sockRafraichissement.send(paquetEnvoye);
			    	   
					}else{	
						    m_sockRafraichissement.setSoTimeout(timeout); // le serveur attend (2s) une reception de paquet de la part du client et s'il ne reçoit ne rien une exception est levée -> on passe au bloc catch 
					    	m_sockRafraichissement.receive(paquetRecu);				    	
					    	System.out.println(m_user.getLogin()+" > vie");
					    	
					    	nbPaquetRecu++;
					    	
					    	if(nbPaquetRecu == limitePaquet)
					    	{
					    		  //reunitialisation des competeurs de paquets reçus et non reçu 
					    		nbPaquetRecu = 0;
					    		nbPaquetNonRecu = 0;
					    		 
					    		  //envoie de la nouvelle liste des connectés
					    		byte[] nouvelleListeConnecte = m_listeUsers.getListeConnectes().getBytes();
					    		paquetEnvoye = new DatagramPacket(nouvelleListeConnecte, nouvelleListeConnecte.length,
					    			                          paquetRecu.getAddress(), paquetRecu.getPort()); 
					    		m_sockRafraichissement.send(paquetEnvoye);
					    	}
					      }
					Thread.sleep(5000);
				}catch(Exception e)
			      {
				     nbPaquetNonRecu++; //si le client ne donne pas signe de vie pendant 10s on incremente le nombre de paquet no reçu
				     System.out.println(m_user.getLogin()+" > paquet no reçu");
			      }
					
	   	      }
	}

	//methode de traitement d'une demande d'une adresse ip
	public void demandeAdrIp()
	{
		try{
			String login = m_in.readLine();  //reception du login de client demandeur d'adresse ip
			
			if(m_listeUsers.estConnecte(login)) // verification de la presence du login dans la liste des connectés
			{
				String loginInterlocutaire = m_in.readLine(); //lecture du login du client pour lequel on veut l'adresse IP
				
				String adrInterlocutaire = m_listeUsers.getAddresseUser(loginInterlocutaire);
				m_out.println(adrInterlocutaire);
			}else{
				   m_out.println("noconnecte");
			     }
			
		}catch(Exception e){
			
		 }
	}

}
